package org.visionlibrary.image.filters.thresholding;

import javax.media.jai.TiledImage;

import org.visionlibrary.image.acquisition.ImageFactory;
import org.visionlibrary.image.model.AbstractFilter;
import org.visionlibrary.image.model.FilterTools;


/*
 * Otsu N. (1979) "A Threshold Selection Method from Gray Level Histograms" 
 * IEEE Trans. on Systems, Man and Cybernetics, 9(1): 62-66
 */
public class SimpleOtsuThreshold extends AbstractFilter {
	protected double total_mean; /* mean gray-level for the whole image */
	protected int threshold = 127;

	protected int foreground = 0; // foreground pixel value (default 0)
	protected int background = 255; // background pixel value (default 255 for
									// 8bpc image)
	
	public SimpleOtsuThreshold() {
	}
		
	public SimpleOtsuThreshold(int foreground, int background) {
		this.foreground = foreground;
		this.background = background;
	}

	@Override
	public TiledImage applyFilter(TiledImage src, TiledImage dest) {
		if (null == src)
			throw new NullPointerException("Source image is null.");

		if (null == dest)
			dest = (new ImageFactory()).createCompatibleImage(src);

		double[] normalizedHistogram = FilterTools.getNormalizedHistogram(
				FilterTools.getHistogram(src, 0), src.getWidth()
						* src.getHeight());
		double[] normalizedCHistogram = FilterTools
				.getCumulativeHistogramDouble(normalizedHistogram);

		double[] mean = new double[normalizedCHistogram.length];
		for (int i = 0; i < mean.length; i++)
			mean[i] +=i * normalizedHistogram[i];
			//mean[i] = mean[i - 1] + i * normalizedHistogram[i];

		total_mean = mean[mean.length - 1];

		int first_bin = 0;
		int last_bin = mean.length - 1;

		for (int i = 0; i < normalizedCHistogram.length; i++) {
			if (Math.abs(normalizedCHistogram[i]) < 0.0000001) {
				first_bin = i;
				break;
			}
		}

		for (int i = mean.length - 1; i >= first_bin; i--) {
			if (Math.abs(1.0 - normalizedCHistogram[i]) < 0.0000001) {
				last_bin = i;
				break;
			}
		}

		threshold = 0;
		double max_sigma = 0.0d;
		double sigma = 0.0d;
		for (int ih = first_bin; ih <= last_bin; ih++) {
			sigma = total_mean * normalizedCHistogram[ih] - mean[ih];
			sigma *= sigma / (normalizedCHistogram[ih] * (1.0 - normalizedCHistogram[ih]));

			if (max_sigma < sigma) {
				max_sigma = sigma;
				threshold = ih;
			}
		}

		int maxX = src.getWidth();
		int maxY = src.getHeight();
		for (int x = 0; x < maxX; x++)
			for (int y = 0; y < maxY; y++) {
				for (int ch = 0; ch < src.getNumBands(); ch++) {
					int pixel = src.getSample(x, y, ch);
					pixel = (pixel < threshold) ? foreground : background;
					dest.setSample(x, y, ch, pixel);
				}
			}

		return dest;
	}
}
